package com.wrl.mmall.common;

import com.wrl.mmall.util.PropertiesUtil;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.util.Hashing;
import redis.clients.jedis.util.Sharded;

import java.util.ArrayList;
import java.util.List;

/**
 * 分布式环境下redis连接池（分片）
 *
 * @author Wang Renlei
 * @Date 2019/10/10 0010
 */
public class RedisShardedPool {

    //Sharded Jedis连接池
    private static ShardedJedisPool pool;
    //最大连接数
    private static Integer maxTotal = Integer.valueOf(PropertiesUtil.getProperty("redis.max.total", "20"));
    //在jedispool中最大的idle状态的jedis实例个数
    private static Integer maxIdle = Integer.valueOf(PropertiesUtil.getProperty("redis.max.idle", "10"));
    //在jedispool中最小的idle状态的jedis实例个数
    private static Integer minIdle = Integer.valueOf(PropertiesUtil.getProperty("redis.min.idle", "2"));
    //在borrow一个实例的时候，是否进行验证操作，如果赋值为true。则得到的jedis实例肯定是可以用的。
    private static Boolean testOnBorrow = Boolean.valueOf(PropertiesUtil.getProperty("redis.test.borrow", "true"));
    //在return一个实例的时候，是否进行验证操作，如果赋值为true。则放回jedispool的jedis实例肯定是可以用的。
    private static Boolean testOnReturn = Boolean.valueOf(PropertiesUtil.getProperty("redis.test.return", "true"));

    //todo 如果是更多是redis，可以用逗号分隔ip，后面再遍历创建对象，对ip和端口进行拆分。
    private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
    private static Integer redis1Prot = Integer.valueOf(PropertiesUtil.getProperty("redis1.port"));
    private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
    private static Integer redis2Prot = Integer.valueOf(PropertiesUtil.getProperty("redis2.port"));
    private static String redisPassword = PropertiesUtil.getProperty("redis.password");

    //只在项目启动的时候初始化一次，并且防止其它人调用
    static {
        initShardedPool();
    }

    //初始化连接池。服务器启动的时候调用一次
    private static void initShardedPool() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);

        //连接耗尽的时候，是否阻塞。
        //true 会阻塞，等待空闲的连接，如果等待超时会抛出异常
        //false 不会阻塞，连接耗尽，直接抛出异常，与上面的异常不一样
        config.setBlockWhenExhausted(true);

        JedisShardInfo info1 = new JedisShardInfo(redis1Ip, redis1Prot, 1000 * 2);
        info1.setPassword(redisPassword);
        JedisShardInfo info2 = new JedisShardInfo(redis2Ip, redis2Prot, 1000 * 2);
        info2.setPassword(redisPassword);

        //因为只有2个，所以指定大小。这里不指定也没问题
        List<JedisShardInfo> jedisShardInfoList = new ArrayList<>(2);
        jedisShardInfoList.add(info1);
        jedisShardInfoList.add(info2);

        /**
         * 当前版本的虚拟节点默认的160个
         * 1 配置项
         * 2 redis对象list
         * 3 分片策略。Hashing.MURMUR_HASH 也是它的默认值，对应的就是一致性算法
         * 4
         */
        pool = new ShardedJedisPool(config, jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);
    }


    //从连接池拿一个实例
    public static ShardedJedis getJedis() {
        return pool.getResource();
    }

}
